﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Exceptions;
using System.Collections;

namespace System
{
	public static class ObjectUtility
	{
		public static void CopyProperties(object source, object destination)
		{
			CannotNullException.Validate("source", source);
			CannotNullException.Validate("destination", destination);

			Type sourceType = source.GetType();
			Type destType = destination.GetType();

			bool isSame = sourceType == destType;

			if(sourceType.IsGenericType && destType.IsGenericType)
			{
				Type itemType = sourceType.GetGenericArguments()[0];
				string listTypeName = "IList`1";
				if (itemType == destType.GetGenericArguments()[0]
					&& sourceType.GetInterface(listTypeName, true) != null
					&& destType.GetInterface(listTypeName, true) != null)
				{
					var getEnumeratorMethod = sourceType.GetMethod("GetEnumerator");
					var addMethod = destType.GetMethod("Add", new Type[] { itemType });
					var clearMethod = destType.GetMethod("Clear");

					if (getEnumeratorMethod != null && addMethod != null && clearMethod != null)
					{
						clearMethod.Invoke(destination, null);

						IEnumerator emu = getEnumeratorMethod.Invoke(source, null) as IEnumerator;
						while (emu.MoveNext())
						{
							addMethod.Invoke(destination, new object[] { emu.Current });
						}
					}
				}
			}

			foreach (PropertyInfo info in sourceType.GetProperties())
			{
				if (info.CanRead && info.CanWrite && info.GetIndexParameters().Length == 0)
				{
					PropertyInfo destInfo = null;
					if (isSame)
					{
						destInfo = info;
					}
					else
					{
						destInfo = destType.GetProperty(info.Name);
						if (!destInfo.CanWrite || !destInfo.CanRead)
						{
							continue;
						}
					}

					destInfo.SetValue(destination, info.GetValue(source, null), null);
				}
			}
		}
	}
}
